/**
* File Name: sigchld01.c
* Auther: Jichang
* Environment: Fedora 7 server
* use sigchld() to catch child process
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<sys/wait.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

// call-back function
// when child-proc die, send SIGCHLD signal.
void catch_child(int signo) 
{
	pid_t wpid;
	int status;

	//wpid = wait(NULL); // recycle

	// use while-loop judge if child-proc die.
	// this loop could recycle couple of process.
	// use wait(): while((wpid = wait(NULL)) != -1) {
	//use waitpid(): the third "0" is non-bloking.
	while((wpid = waitpid(-1, &status, 0)) != -1) {
		if(WIFEXITED(status)) {
			printf("catch child id: %d, ret: %d\n", wpid, WEXITSTATUS(status));
		}
	}

	return;
}

int main(int argc, char *argv[])
{
	pid_t pid;
	int i;
	// block.
	// ask for 5 child process
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set, SIGCHLD);
	sigprocmask(SIG_BLOCK, &set, NULL);//set block

	for(i = 0; i < 15; i++) {
		if((pid = fork()) == 0) {
			break;
		}
	}

	if(i == 15) {
		// recycle child process is only for father
		struct sigaction act;

		//init
		act.sa_handler = catch_child; // call-back function.
		sigemptyset(&act.sa_mask); //make empty.
		act.sa_flags = 0;  //set defualt 
		
		// regist signal-catch function.
		sigaction(SIGCHLD, &act, NULL);

		// relieve block.
		// if don't relieve, function does not have chance to call.
		sigprocmask(SIG_UNBLOCK, &set, NULL);

		// father process.
		printf("I am parent, pid = %d\n", getpid());

		// make it more possibly happend.
		while(1); // wait catch child process
		
	} else {
		// sleep(1); //wait for father.
		// child process.
		printf("I am child, pid = %d\n", getpid());
		return i;
	}
	
	return 0;
}

